#include "xmpp.h"
#include "server.h"
#include <string.h>


extern struct infoClient clients[MAX_CLIENTS];
extern int maxcli;


static int id = 0;


/* fonctions balises ouvrantes */
void (*elements[27])(void *data, const char *name, const char **atts) = {
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* @ A B */
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* C D E */
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* F G H */
  &XmppIqStanza,   &XmppMuet,   &XmppMuet, /* I J K */
  &XmppMuet,   &XmppMessage,   &XmppMuet, /* L M N */
  &XmppMuet,   &XmppMuet,   &XmppQuery, /* O P Q */
  &XmppMuet,   &XmppStream, &XmppMuet, /* R S T */
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* U V W */
  &XmppMuet,   &XmppMuet,   &XmppMuet  /* X Y Z */
};


/* fonctions balises fermantes */
void (*endElements[27])(void *data, const char *name) = {
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* @ A B */
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* C D E */
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* F G H */
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* I J K */
  &XmppMuet,   &XmppCloseMessage,   &XmppMuet, /* L M N */
  &XmppMuet,   &XmppMuet,   &XmppCloseQuery, /* O P Q */
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* R S T */
  &XmppMuet,   &XmppMuet,   &XmppMuet, /* U V W */
  &XmppMuet,   &XmppMuet,   &XmppMuet  /* X Y Z */
};




/* cherche un attribut d'une balise */
char * find_attribute(const char *name, const char **atts)
{
  int j;
  char *p;
	
  for (j = 0; atts[j]; j+=2)
    {
      if (!strcasecmp(atts[j], name))
	{
	  p = (char *) malloc(strlen(atts[j+1]));
	  strcpy(p, atts[j+1]);
	  return p;
	}
    }
  return NULL;
}


void XmppMuet(){
  fprintf(stdout, "Muet\n");
  return ;
}


void XMLCALL
processingInstruction(void *userData, const char * target, const char * data)
{

  fprintf(stdout, "\n*************************************** target = %s \n", target);
  fprintf(stdout, "\n data = %s \n", data);

}


/* actions en début de balise */
void XMLCALL
startElement(void *data, const char *name, const char **atts)
{ 
  fprintf(stdout, "balise %s\n", name);

  struct infoClient * own = (struct infoClient *) data;
  
  own->lastTag = (char *)malloc((strlen(name) + 1) * sizeof(char));

  strcpy(own->lastTag, name); /* positionner lastBalise */
 
  elements[hash_balise(name)](data, name, atts);


}


/* actions en fin de balise */
void XMLCALL
endElement(void *data, const char *name)
{
  fprintf(stdout, "sortie balise %s\n", name);
  endElements[hash_balise(name)](data, name);
}


/* actions à la rencontre d'un texte */
void HandlerCharacterData(void *data, const char *text, int len){

  struct infoClient * own = (struct infoClient *) data;  

  if(!strncmp(own->lastTag, "username", 8) && (own->username == NULL)){ /* sauvegarder le username */
    /* le != NULL pour éviter les surprises de expat*/
    own->username = (char *)malloc((len+1) * sizeof(char));

    strncpy(own->username, text, len);
    own->username[len]='\0';

  }

  if(!strncmp(own->lastTag, "password", 8)){ /* sauvegarder le password */
    own->password = (char *)malloc((len+1) * sizeof(char));

    strncpy(own->password, text, len);
    own->password[len]='\0';
  }


  /* recuperer le message entre <body> et </body>*/
  if(!strncmp(own->lastTag, "body", 4)){ /* sauvegarder le password */
    own->body = (char *)malloc((len+1) * sizeof(char));

    strncpy(own->body, text, len);
    own->body[len]='\0';
  }

  
  
}


/* traitement lors de l'ouverture d'un stream */
void XMLCALL
XmppStream(void *data, const char *name, const char **atts){

  struct infoClient * own = (struct infoClient *) data;



  own->to = find_attribute("to", atts);
  own->from = find_attribute("from", atts);
  own->id = find_attribute("id", atts);
  own->xml_lang = find_attribute("xml:lang", atts);
  own->version = find_attribute("version", atts);  
  own->xmlns = find_attribute("xmlns", atts);  
  own->xmlns_stream = find_attribute("xmlns:stream", atts);  

  
  fprintf(stdout, "to =  %s\n", own->to);
  fprintf(stdout, "from =  %s\n", own->from);
  fprintf(stdout, "id =  %s\n", own->id);
  fprintf(stdout, "xml:lang =  %s\n", own->xml_lang);
  fprintf(stdout, "version =  %s\n", own->version);
  fprintf(stdout, "xmlns =  %s\n", own->xmlns);
  fprintf(stdout, "xmlns:stream =  %s\n", own->xmlns_stream);

  
  /* repondre au client */


  /* <stream:stream ... > */
  char reponse[1000];

  
  char hostname[100]; gethostname(hostname, 100);

  sprintf(reponse, "<stream:stream version='1.0' id='%d' from='%s' xmlns:stream='%s' xmlns='%s'>", 
	  ++id, hostname, "http://etherx.jabber.org/streams", "jabber:client");

  
  sprintf(&(reponse[strlen(reponse)]),"<stream:features><register xmlns='http://jabber.org/features/iq-register'/><auth xmlns='http://jabber.org/features/iq-auth'/><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>CRAM-MD5</mechanism></mechanisms></stream:features>");
    
  
  if(write(own->desc, reponse, strlen(reponse)) == -1){
    perror("write");
    exit(EXIT_SUCCESS);
  }

  
}


/* traitement du stanza iq */
void XMLCALL
XmppIqStanza(void *data, const char *name, const char **atts){
  
  struct infoClient * own =   (struct infoClient *)data;

  if(!strncmp(name, "iq", 2)){ // = <iq ...
    
    /* trouver les attribut id et type */
    own->iq_type = find_attribute("type", atts);
    own->iq_id = find_attribute("id", atts);

  }

}


/* traitement de la balise <query sous la balise <iq */
void XMLCALL
XmppQuery(void *data, const char *name, const char **atts){
  
  struct infoClient * own =   (struct infoClient *)data;
  
  if(!strncmp(name, "query", 5)){ //<query ...
    /* trouver l'opération */
    
    own->curentOperation = find_attribute("xmlns", atts);
    
    /* on suppose que xmlns='jabber:iq:OPERATION' */
    
    /* tester si operation = enregistrement && demande : type='get' */
    if(!strncmp(&(own->curentOperation[strlen("jabber:iq:")]), "register", strlen("register"))
       && (!strncmp(own->iq_type, "get", 3))){
	 
	 /* construire la réponse */
	 char reponse[1000];
	 sprintf(reponse, "<iq type='result' id='%s'><query xmlns='jabber:iq:register'><password/><instructions>Choose a username and password to register with this server.</instructions><username/></query></iq>", own->iq_id);
	 
	 /* ecrire la reponse sur la socket */
	 if(write(own->desc, reponse, strlen(reponse)) == -1){
	   perror("write");
	   exit(EXIT_SUCCESS);
	 }        
       }

    /* tester si operation = enregistrement && demande : type='set' */
    if(!strncmp(&(own->curentOperation[strlen("jabber:iq:")]), "register", strlen("register")) 
       && (!strncmp(own->iq_type, "set", 3))){

	 /* recuperer le username + password */
	 
	 
      }    
  }
}


/* a la rencontre d'une balise fermante query */
void XMLCALL
XmppCloseQuery(void *data, const char *name){

  struct infoClient * own =   (struct infoClient *)data;


  if(!strncmp(name, "query", 5)){ //</query>
    
    /* tester si operation = enregistrement && demande : type='set' */
    if(!strncmp(&(own->curentOperation[strlen("jabber:iq:")]), "register", strlen("register"))
       && (!strncmp(own->iq_type, "set", 3))){

      /* delete curent_operation */
      strcpy(own->curentOperation, "");

      /* repondre que l'opération s'est passée avec succès */
      	 char reponse[1000];
	 sprintf(reponse,"<iq type='result' id='%s'/>", own->iq_id);

	 /* ecrire la reponse sur la socket */
	 if(write(own->desc, reponse, strlen(reponse)) == -1){
	   perror("write");
	   exit(EXIT_SUCCESS);
	 }

    }

  }

}




/*********************************************************************

<message to="toto@jabber.org" type="chat">
	<body>Salut !</body>
</message>
*/


/* à la rencontre de la balise message  */
void XMLCALL
XmppMessage(void *data, const char *name, const char ** atts){
  
  struct infoClient * own =   (struct infoClient *)data;
  
  // TODO il faut aussi verficier qu'on est bien dans l'état d'envoi de messages
  if(!strncmp(name, "message", 8)){ //<message>   
    
    strcpy(own->curentOperation, "message");
    
    own->message_to = find_attribute("to", atts);
    own->message_type = find_attribute("type", atts);    
    
  }

}

/* à la rencontre de la balise de fin d'un message </message */
void XMLCALL
XmppCloseMessage(void *data, const char *name){
  
  struct infoClient * own =   (struct infoClient *)data;
  
  // TODO il faut aussi verficier qu'on est bien dans l'état d'envoi de messages
  if(!strncmp(name, "message", 8)){ //<message>   
    
    /* envoyer le message au au destinataire */

    /* trouver le destinataire du message */
    int desc = search_receiver(own->message_to);    

    /* ecrire le message dans sa socket */
    char reponse[1000];
    sprintf(reponse,"<message from='%s' type='%s'><body>%s</body></message>", 
	    own->username, own->message_type, own->body);
    
    fprintf(stdout, "%s", reponse);

    /* ecrire la reponse sur la socket */
    if(write(desc, reponse, strlen(reponse)) == -1){
      perror("write");
      exit(EXIT_SUCCESS);
    }

  }

}


/*******************************************************************/

int search_receiver(const char * receiver){
  
  int i;

  fprintf(stdout,"UUUUUUUUUU   %d   UUUUUUUUUUUUUUUUU %s \n", maxcli, receiver);

  fflush(stdout);

  
  for(i=0; i<maxcli; i++){

    fprintf(stdout,"je rentre une fois ");
    fflush(stdout);

    if(!strncmp(receiver, clients[i].username, strlen(receiver))){
      return clients[i].desc;
    }

    fprintf(stdout,"je sors une fois ");

  }
  
  return (-1);

}
